Skip to content

Conversation

@julbd
Copy link

@julbd julbd commented Aug 12, 2025

Add SCHEMA_SYNC_PATH environment variable allowing to specify/customize the path to schema-sync. By default : (Directus root)/schema-sync

Note : Initially, I planned to add a global cli option --path, but the current implementation with both global and command-scoped variables required too much refactoring.

@julbd julbd changed the title Add specifying path to schema-sync folder with SCHEMA_SYNC_PATH Allow specifying path to schema-sync folder with SCHEMA_SYNC_PATH Aug 12, 2025
@julbd
Copy link
Author

julbd commented Aug 13, 2025

@u12206050

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds support for customizing the path to the schema-sync folder via the SCHEMA_SYNC_PATH environment variable. By default, it continues to use (Directus root)/schema-sync.

Changes:

  • Introduced SCHEMA_SYNC_PATH environment variable to allow customization of the schema-sync folder location
  • Refactored ExportHelper static class into instance-based ExportMeta class to support configurable paths
  • Updated all components (SchemaExporter, CollectionExporter, ExportManager) to accept and use the configurable path

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/exportMeta.ts New file containing the ExportMeta class with path configuration support, moved from ExportHelper in utils.ts
src/utils.ts Removed ExportHelper class, extracted fileExists as a standalone utility function
src/types.ts Added optional path parameter to CollectionExporterOptions
src/index.ts Updated to instantiate ExportMeta with SCHEMA_SYNC_PATH and pass path to all exporters
src/exportManager.ts Added path parameter to constructor and passes it to collection exporters
src/schemaExporter.ts Added path option to constructor and uses ExportMeta for directory resolution
src/collectionExporter.ts Updated to use ExportMeta with configurable path instead of static ExportHelper
src/updateManager.ts Removed unused ExportHelper import
README.md Added documentation for SCHEMA_SYNC_PATH environment variable and cleaned up trailing whitespace

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +6 to +10
public schemaDir: string;

constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation detected. The code uses spaces instead of tabs, which is inconsistent with the rest of the file. Lines 6-10 should use tabs for indentation to match the project's style.

Suggested change
public schemaDir: string;
constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}
public schemaDir: string;
constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +61
public schemaDir: string;

constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}

get dataDir() {
return resolve(this.schemaDir, 'data');
}

get hashFile() {
return resolve(this.schemaDir, 'hash.txt');
}

async updateExportMeta() {
const hasher = createHash('sha256');
const files = await readdir(this.dataDir);
for (const file of files) {
if (file.endsWith('.json')) {
const json = await readFile(`${this.dataDir}/${file}`, { encoding: 'utf8' });
hasher.update(json);
}
}
const hash = hasher.digest('hex');

const { hash: previousHash } = await this.getExportMeta() || {};

// Only update hash file if it has changed
if (hash === previousHash) return false;

const ts = utcTS();
const txt = hash + '@' + ts;

await writeFile(this.hashFile, txt);
return {
hash,
ts,
};
}

async getExportMeta() {
try {
const content = await readFile(this.hashFile, { encoding: 'utf8' });
const [hash, ts] = content.split('@');

if (hash && ts && new Date(ts).toString() !== 'Invalid Date') {
return {
hash,
ts,
};
}
} catch {
// ignore
}
return null;
}
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation detected. The code uses spaces instead of tabs. The entire class body (lines 20-61) should use tabs for indentation to match the project's style.

Suggested change
public schemaDir: string;
constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}
get dataDir() {
return resolve(this.schemaDir, 'data');
}
get hashFile() {
return resolve(this.schemaDir, 'hash.txt');
}
async updateExportMeta() {
const hasher = createHash('sha256');
const files = await readdir(this.dataDir);
for (const file of files) {
if (file.endsWith('.json')) {
const json = await readFile(`${this.dataDir}/${file}`, { encoding: 'utf8' });
hasher.update(json);
}
}
const hash = hasher.digest('hex');
const { hash: previousHash } = await this.getExportMeta() || {};
// Only update hash file if it has changed
if (hash === previousHash) return false;
const ts = utcTS();
const txt = hash + '@' + ts;
await writeFile(this.hashFile, txt);
return {
hash,
ts,
};
}
async getExportMeta() {
try {
const content = await readFile(this.hashFile, { encoding: 'utf8' });
const [hash, ts] = content.split('@');
if (hash && ts && new Date(ts).toString() !== 'Invalid Date') {
return {
hash,
ts,
};
}
} catch {
// ignore
}
return null;
}
public schemaDir: string;
constructor(schemaDir?: string) {
this.schemaDir = resolve(process.cwd(), schemaDir ?? 'schema-sync')
}
get dataDir() {
return resolve(this.schemaDir, 'data');
}
get hashFile() {
return resolve(this.schemaDir, 'hash.txt');
}
async updateExportMeta() {
const hasher = createHash('sha256');
const files = await readdir(this.dataDir);
for (const file of files) {
if (file.endsWith('.json')) {
const json = await readFile(`${this.dataDir}/${file}`, { encoding: 'utf8' });
hasher.update(json);
}
}
const hash = hasher.digest('hex');
const { hash: previousHash } = await this.getExportMeta() || {};
// Only update hash file if it has changed
if (hash === previousHash) return false;
const ts = utcTS();
const txt = hash + '@' + ts;
await writeFile(this.hashFile, txt);
return {
hash,
ts,
};
}
async getExportMeta() {
try {
const content = await readFile(this.hashFile, { encoding: 'utf8' });
const [hash, ts] = content.split('@');
if (hash && ts && new Date(ts).toString() !== 'Invalid Date') {
return {
hash,
ts,
};
}
} catch {
// ignore
}
return null;
}

Copilot uses AI. Check for mistakes.


export function utcTS(isoTimestamp: string = new Date().toISOString()) {
return isoTimestamp.replace('T', ' ').replace(/\.\d*Z/, '');
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent indentation detected. The function uses spaces instead of tabs. Line 66 should use tabs for indentation to match the project's style.

Suggested change
return isoTimestamp.replace('T', ' ').replace(/\.\d*Z/, '');
return isoTimestamp.replace('T', ' ').replace(/\.\d*Z/, '');

Copilot uses AI. Check for mistakes.
constructor(
protected path: string,
protected logger: ApiExtensionContext['logger']
) { }
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra whitespace in the constructor body. The closing brace should be on the line after the constructor parameters without extra whitespace or tabs before it.

Suggested change
) { }
) {}

Copilot uses AI. Check for mistakes.
query?: Pick<Query, 'filter' | 'sort' | 'limit'>;

// Path to the export folder
path?: string,
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing trailing comma after the path property. Add a comma for consistency with the project's code style and to prevent potential issues when adding more properties.

Suggested change
path?: string,
path?: string;

Copilot uses AI. Check for mistakes.

constructor(protected logger: ApiExtensionContext['logger']) {}
constructor(
protected path: string,
Copy link

Copilot AI Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter should accept string | undefined instead of just string. The value env.SCHEMA_SYNC_PATH being passed can be undefined when the environment variable is not set, which should be a valid scenario based on the documentation showing the default value.

Suggested change
protected path: string,
protected path: string | undefined,

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant